//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include "private.h"
#include <buola/dbus/cproxy.h>

namespace buola { namespace dbus {

struct CMessage::SIterator
{
    DBusMessageIter mIter;
    SIterator *mParent;
};

CMessage::CMessage(NMethodCall,const CProxy &pProxy,const std::string &pInterface,const std::string &pMethod)
    :   mP(new SPrivate)
    ,   mBus(pProxy.GetBus())
{
    mP->mMessage=dbus_message_new_method_call(pProxy.GetService().c_str(),pProxy.GetPath().c_str(),
                                              pInterface.empty()?nullptr:pInterface.c_str(),pMethod.c_str());
    
    dbus_message_set_auto_start(mP->mMessage,true);
    
    mIterator=new SIterator;
    mIterator->mParent=nullptr;
    dbus_message_iter_init_append(mP->mMessage,&mIterator->mIter);
}

CMessage::CMessage(NEmptyReply,CBus *pBus,uint32_t pSerial)
    :   mP(new SPrivate)
    ,   mBus(pBus)
    ,   mExpectedSerial(pSerial)
{
    mP->mMessage=nullptr;

    mIterator=nullptr;
}

CMessage::CMessage(NReceived,CBus *pBus,void *pMessage)
    :   mP(new SPrivate)
    ,   mBus(pBus)
{
    mP->mMessage=(DBusMessage*)pMessage;

    mIterator=new SIterator;
    mIterator->mParent=nullptr;
    dbus_message_iter_init(mP->mMessage,&mIterator->mIter);
}

CMessage::CMessage(NNewReply,CMessage *pReplyTo)
    :   mP(new SPrivate)
    ,   mBus(pReplyTo->mBus)
{
    mP->mMessage=dbus_message_new_method_return(pReplyTo->mP->mMessage);
    
    mIterator=new SIterator;
    mIterator->mParent=nullptr;
    dbus_message_iter_init_append(mP->mMessage,&mIterator->mIter);
}

CMessage::CMessage(NNewError,CMessage *pReplyTo,const std::string &pName,const std::string &pMsg)
    :   mP(new SPrivate)
    ,   mBus(pReplyTo->mBus)
{
    mP->mMessage=dbus_message_new_error(pReplyTo->mP->mMessage,pName.c_str(),pMsg.c_str());
    
    mIterator=new SIterator;
    mIterator->mParent=nullptr;
    dbus_message_iter_init_append(mP->mMessage,&mIterator->mIter);
}

void CMessage::FillEmpty(void *pMessage)
{
    mP->mMessage=(DBusMessage*)pMessage;

    mIterator=new SIterator;
    mIterator->mParent=nullptr;
    dbus_message_iter_init(mP->mMessage,&mIterator->mIter);
}

CMessage::~CMessage()
{
    delete mIterator;
    if(!mP->mMessage)
        mBus->GetQueuedReply(mExpectedSerial);
        
}

char CMessage::CanExtract()
{
    return (char)dbus_message_iter_get_arg_type(&mIterator->mIter);
}

void CMessage::AppendBasic(char pType,void *pV)
{
    dbus_message_iter_append_basic(&mIterator->mIter,pType,pV);
}

void CMessage::Skip()
{
    dbus_message_iter_next(&mIterator->mIter);
}

void CMessage::ExtractBasic(char pType,void *pV)
{
    char lType=(char)dbus_message_iter_get_arg_type(&mIterator->mIter);
    if(lType!=pType)
    {
        if(lType==DBUS_TYPE_VARIANT)
        {
            OpenContainer();
            ExtractBasic(pType,pV);
            CloseContainer();
            return;
        }
        if(!(pType==DBUS_TYPE_STRING&&lType==DBUS_TYPE_OBJECT_PATH))
            throw XWrongParams("wrong type in CMessage::ExtractBasic");
    }

    dbus_message_iter_get_basic(&mIterator->mIter,pV);
    dbus_message_iter_next(&mIterator->mIter);
}

void CMessage::StartArray(const char *pSig)
{
    SIterator *lNew=new SIterator;

    lNew->mParent=mIterator;

    if(!dbus_message_iter_open_container(&mIterator->mIter,DBUS_TYPE_ARRAY,strdup(pSig),&lNew->mIter))
        throw XInternal("can't open dbus container");

    mIterator=lNew;
}

void CMessage::EndArray()
{
    SIterator *lThis=mIterator;
    mIterator=lThis->mParent;

    dbus_message_iter_close_container(&mIterator->mIter,&lThis->mIter);

    delete lThis;
}

    void CMessage::StartDictEntry()
{
    SIterator *lNew=new SIterator;

    lNew->mParent=mIterator;

    dbus_message_iter_open_container(&mIterator->mIter,DBUS_TYPE_DICT_ENTRY,nullptr,&lNew->mIter);

    mIterator=lNew;
}

void CMessage::EndDictEntry()
{
    SIterator *lThis=mIterator;
    mIterator=lThis->mParent;

    dbus_message_iter_close_container(&mIterator->mIter,&lThis->mIter);

    delete lThis;
}

void CMessage::StartStruct()
{
    SIterator *lNew=new SIterator;

    lNew->mParent=mIterator;

    dbus_message_iter_open_container(&mIterator->mIter,DBUS_TYPE_STRUCT,nullptr,&lNew->mIter);

    mIterator=lNew;
}

void CMessage::EndStruct()
{
    SIterator *lThis=mIterator;
    mIterator=lThis->mParent;

    dbus_message_iter_close_container(&mIterator->mIter,&lThis->mIter);

    delete lThis;
}

void CMessage::OpenContainer()
{
    SIterator *lNew=new SIterator;
    lNew->mParent=mIterator;
    dbus_message_iter_recurse(&mIterator->mIter,&lNew->mIter);
    mIterator=lNew;
}

void CMessage::CloseContainer()
{
    SIterator *lThis=mIterator;
    mIterator=lThis->mParent;
    delete lThis;
    dbus_message_iter_next(&mIterator->mIter);
}

std::string CMessage::GetSender()
{
    return std::string(dbus_message_get_sender(mP->mMessage));
}

std::string CMessage::GetPath()
{
    return std::string(dbus_message_get_path(mP->mMessage));
}

std::string CMessage::GetMember()
{
    return std::string(dbus_message_get_member(mP->mMessage));
}

std::string CMessage::GetInterface()
{
    return std::string(dbus_message_get_interface(mP->mMessage));
}

bool CMessage::IsError()
{
    return dbus_message_get_type(mP->mMessage)==DBUS_MESSAGE_TYPE_ERROR;
}

PMessage CMessage::BuildReply()
{
    return PMessage(new CMessage(nNewReply,this));
}

PMessage CMessage::BuildError(const std::string &pName,const std::string &pMsg)
{
    return PMessage(new CMessage(nNewError,this,pName,pMsg));
}

void CMessage::Send()
{
    mBus->SendMessage(this);
}

/*namespace dbus*/ } /*namespace buola*/ }
